package polymorphism;

class Animal {
    public String name;

    public boolean isSameName(Animal a) {
        return this.name.equals(a.name);
    }

    public Animal getAnimal() {
        return new Cat();
    }

    Animal() {
        this.name = "无名请赋值";
    }

    Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + "在吃东西");
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}

//重写：
//1. 方法名相同
//2. 参数列表相同(类型 数量 顺序)
//3. 返回值相同或依旧是父子关系
//4. 对于访问权限修饰符来说 子类 >= 父类 (private < default < protected < public)
//5. 父类中被 private static final 修饰的方法不能重写
class Dog extends Animal {
    public boolean silly;

    Dog() {
        super();
        this.silly = false;
    }

    Dog(String name, boolean silly) {
        super(name);
        this.silly = silly;
    }

    @Override //在重写时可以加上注解 @Override 辅助我们正确的写出重写
    public void eat() {
        System.out.println(name + "在吃骨头");
    }
}

class Cat extends Animal {
    public boolean cute;

    public Cat() {
        super();
        this.cute = true;
    }

    public Cat(String name, boolean cute) {
        super(name);
        this.cute = cute;
    }

    @Override
    public void eat() {
        System.out.println(name + "在吃鱼");
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", cute=" + cute +
                '}';
    }
}

///////////////////////////////////////////////
class A{
    A(){
        func();
    }
    public void func(){
        System.out.println("A.func()");
    }
}
class B extends A{
    int num=666;

    @Override
    public void func() {
        System.out.println("B.func()"+" num="+num);
    }
}
///////////////////////////////////////////////
public class Test {
    public static void main3(String[] args) {
        //避免在构造方法中调用重写的方法
        B b=new B();//B.func() num=0
        //调用了B的func 但并未将num初始化为666 为啥?
        //因为B重写了func 构成了动态绑定 当实例化B的过程中
        //当调用无参构造函数时就调用了子类B的func 但此时B还未实例化完成 故num的值用默认值0代替

        //为了避免上述不易发现 难以排查的bug出现 故我们因该编写尽可能简单的构造方法来实例化对象
        //从而避免在构造方法中调用 重写的方法 的发生
    }

    public static void main2(String[] args) {
        //在调用同一个方法时 我们希望针对某些对象应该有不同的行为 那么这时该怎么办呢?
        //多态：
        /*
         * 在面向对象编程语言中，多态是指同一个接口或同一个抽象类，可以有多个实现方式，
         * 这些不同的实现方式可以被看做是同一个类型。在执行时，根据实际传入的参数类型，
         * 选择调用不同的实现方式，从而实现了同一个方法在不同情况下的不同处理方式。
         * 多态有助于提高代码的复用性和可扩展性，是面向对象编程的重要特性之一。
         */

        //多态的实现条件(以下条件缺一不可)：
        /*
         * 1.必须在继承体系下
         * 2.子类必须对父类中的相应方法进行重写
         * 3.通过父类的引用调用重写的方法
         */
        Dog d1 = new Dog("旺财", false);
        Cat c1 = new Cat("花海", true);

        Animal a1 = d1;
        Animal a2 = c1;

        a1.eat();//旺财在吃骨头
        a2.eat();//花海在吃鱼

    }

    public static void main(String[] args) {
        //向上转型
        //1. 将子类的实例对象 赋给 父类的引用
        Dog dog1 = new Dog("旺财", false);
        Animal a1 = dog1;
        Cat cat1 = new Cat("咪咪", true);

        //2.将子类对象作为方法的实参 而父类作为参数类型
        System.out.println(a1.isSameName(cat1));//false

        //3.方法的返回类型为父类 但实际返回的实体是子类
        Animal a2 = a1.getAnimal();
        System.out.println(a2);//Cat{name='无名请赋值', cute=true}

        //向下转型 同上
        //但是不安全
        Animal a3 = new Cat("花花", true);
        Cat c1 = (Cat) a3;//需要强制类型转换
        System.out.println(c1);//Cat{name='花花', cute=true}
        //由于a3 本来就是一个Cat 所以上述代码没毛病

        Animal a4 = new Animal("动物");
        //Cat c2 = (Cat) a4;
        //System.out.println(c2);
        /*
         * ClassCastException: class polymorphism.Animal cannot be cast to class polymorphism.Cat
         * 类映射异常            polymorphism包下的类Animal 无法被映射为 polymorphism包下的类Cat
         */
        //应将上述代码改为 来避免报错
        if (a4 instanceof Cat) {//instanceof 用来判断他前面的实例对象 与后面的类是否相同 相同true 不同false
            Cat c2 = (Cat) a4;
            System.out.println(c2);
        }
        //啥也没输出 因为 a4 instanceof Cat 是 false
    }
}
